package com.hibegin.ftp;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.text.DecimalFormat;

public class FtpSocketUtil implements IFtpSocket, InvocationHandler {

    private Object obj;
    private FtpSession ftpSession;

    public FtpSocketUtil(Object obj, FtpSession ftpSession) {
        this.obj = obj;
        this.ftpSession = ftpSession;
    }

    public FtpSocketUtil(FtpSession ftpSession) {
        this.ftpSession = ftpSession;
    }

    private static byte[] subBytes(byte[] b, int start, int length) {
        byte rbytes[] = new byte[length];
        System.arraycopy(b, start, rbytes, 0, length);
        return rbytes;
    }

    public static String formatFileSize(long fileS) {// 转换文件大小
        DecimalFormat df = new DecimalFormat("#.0");
        String fileSizeString;
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + "B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + "K";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + "M";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + "G";
        }
        return fileSizeString;
    }

    public static IFtpSocket getProxy(FtpSession ftpSession) {
        IFtpSocket ftpSocketUtil = new FtpSocketUtil(ftpSession);
        InvocationHandler ftpIFtpSocket = new FtpSocketUtil(ftpSocketUtil, ftpSession);
        return (IFtpSocket) Proxy.newProxyInstance(ftpIFtpSocket.getClass()
                        .getClassLoader(), ftpIFtpSocket.getClass().getInterfaces(),
                ftpIFtpSocket);
    }

    /*
     * (non-Javadoc)
     *
     * @see IFtpSocket#printOutStr()
     */
    @Override
    public void printOutStr() {
        Socket socket;
        try {
            socket = new Socket(ftpSession.getFtpUser().getAddr(), (int) ftpSession.getAttr().get("port"));
            System.out.println(IOUtil.getStringInputStream(socket
                    .getInputStream()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see IFtpSocket#getFile(java.io.File)
     */
    @Override
    public void getFile(final File file) {
        Socket socket;
        try {
            socket = new Socket(ftpSession.getFtpUser().getAddr(), (int) ftpSession.getAttr().get("port"));
            IOUtil.writeToFile(socket.getInputStream(), file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see IFtpSocket#putFile(java.io.File)
     */
    @Override
    public void putFile(final File file) {
        Socket socket;
        try {
            socket = new Socket(ftpSession.getFtpUser().getAddr(), (int) ftpSession.getAttr().get("port"));
            byte[] bytes = IOUtil.getByteByInputStream(new FileInputStream(file));
            int part = 100;
            int last = bytes.length % part;
            int block = (bytes.length - last) / part;
            if (block > 0) {
                int sendSize = 0;
                for (int i = 0; i < part; i++) {
                    socket.getOutputStream().write(subBytes(bytes, i * block, block));
                    sendSize += block;
                    System.out.println("uploadSize " + formatFileSize(sendSize) + " -> " + (i + 1) + "%");
                }
            }
            if (last != 0) {
                socket.getOutputStream().write(subBytes(bytes, block * part, last));
            }
            socket.getOutputStream().close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(ftpSession.getFtpUser().getAddr(), (int) ftpSession.getAttr().get("port"));
        try {
            // 打开socket通道
            SocketChannel socketChannel = SocketChannel.open();
            // 设置为非阻塞方式
            socketChannel.configureBlocking(false);
            // 打开选择器
            Selector selector = Selector.open();
            Iterator iterator;
            Set<SelectionKey> selectionKeys;
            // 注册连接服务端socket动作
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            // 连接
            socketChannel.connect(SERVER_ADDRESS);
            // 分配缓冲区大小内存
            while (true) {
                // 选择一组键，其相应的通道已为 I/O 操作准备就绪。
                // 此方法执行处于阻塞模式的选择操作。
                selector.select();
                // 返回此选择器的已选择键集。
                selectionKeys = selector.selectedKeys();
                iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = (SelectionKey) iterator.next();
                    SocketChannel client = (SocketChannel) selectionKey.channel();
                    if (selectionKey.isConnectable()) {
                        if (client.isConnectionPending()) {
                            client.finishConnect();
                            System.out.println("connect finished");
                            byte[] bytes = IOUtil.getByteByInputStream(new FileInputStream(file));
                            ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
                            int count = 0;
                            while (byteBuffer.hasRemaining() && client.isOpen()) {
                                int len = client.write(byteBuffer);
                                count += len;
                                if (len < 0) {
                                    throw new EOFException();
                                } else {
                                    System.out.println("uploadSize " + formatFileSize(byteBuffer.position()) + " -> " + ((int) Math.ceil((byteBuffer.position() / (bytes.length * 1.00)) * 100)) + "%");
                                }
                            }
                            System.out.println("count ______________" + count);
                            client.shutdownOutput();
                            return;
                        }
                    }
                    selectionKey.channel();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }*/
    }

    @Override
    public Object invoke(final Object proxy, final Method method,
                         final Object[] args) throws Throwable {
        new Thread() {
            @Override
            public void run() {
                try {
                    FtpRequestImpl.lock();
                    ftpSession.getAttr().put("port", 0);
                    ftpSession.sendMsg("PASV");
                    while ((int) ftpSession.getAttr().get("port") == 0) {
                        Thread.sleep(100);
                    }
                    method.invoke(obj, args);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    FtpRequestImpl.unLock();
                }
            }
        }.start();
        return null;
    }
}
